namespace Hack.Decompiling.PEA.PE
{
  using System;
  using System.IO;
  using System.Diagnostics;
  using System.Collections.Generic;
  using System.Text;

  public struct IATDirectory : DataDirectory
  {
    #region Statics
    public static IATDirectory it()
    {
      return new IATDirectory();
    }
    public static IATDirectory from(BinaryReader reader, NTHeader header)
    {
      return (IATDirectory)it().Read(reader, header);
    }
    #endregion
    #region Fields
    public ImportLookupEntry[] _ImportAddresses;
    #endregion
    #region Internal Fields
    internal ImageDataDirectory _Entry;
    #endregion
    #region Properties
    public ImageDataDirectory Entry
    {
      get
      {
        return this._Entry;
      }
      set
      {
        this._Entry = value;
      }
    }
    public ImportLookupEntry[] ImportAddresses
    {
      get
      {
        return this._ImportAddresses;
      }
      set
      {
        this._ImportAddresses = value;
        if (value != null && value.Length>0)
        {
          ImportLookupEntry first = value[0];
          if (first.IsValid)
          {
            this._Entry.VirtualSize = (UInt32)((first.IsPE32 ? 4 : 8) * value.Length);
          }
        }
        else
        {
          this._Entry.VirtualSize = 0;
        }
      }
    }
    #endregion
    #region Methods
    #region Write/Read
    public DataDirectory Read(BinaryReader reader, NTHeader header)
    {
      if (reader != null)
      {
        this._Entry = header[DirectoryEntries.IAT];

        Int64 current = Utils.GetPosition(reader);

        UInt32 start = header.GetFilePointer(this.Entry.VirtualAddress);

        if (start != 0 && Utils.SetPosition(reader, start))
        {
          this._ImportAddresses = this.ReadEntries(reader, header, this.Entry.VirtualAddress,this.Entry.VirtualSize);

          Utils.SetPosition(reader, current);
        }
      }
      return this;
    }
    public DataDirectory Write(BinaryWriter writer, NTHeader header)
    {
      if (writer != null)
      {
        header[DirectoryEntries.IAT] = this._Entry;

        Int64 current = Utils.GetPosition(writer);

        UInt32 start = header.GetFilePointer(this.Entry.VirtualAddress);

        if (start != 0 && Utils.SetPosition(writer, start))
        {
          this.WriteEntries(writer, header, this.Entry.VirtualAddress, this._ImportAddresses);

          Utils.SetPosition(writer, current);
        }
      }
      return this;
    }
    #endregion
    #region Internals
    internal ImportLookupEntry[] ReadEntries(BinaryReader reader, NTHeader header, UInt32 RVA,UInt32 size)
    {
      ImportLookupEntry[] entries = null;

      if (reader != null)
      {
        Int64 current = Utils.GetPosition(reader);
        UInt32 start = header.GetFilePointer(RVA);
        if (start != 0 && Utils.SetPosition(reader, start))
        {
          List<ImportLookupEntry> lookups = new List<ImportLookupEntry>();

          for (UInt32 i = 0; i < size; i += (UInt32)(header.IsPE64 ? 8 : 4))
          {
            ImportLookupEntry entry = ImportLookupEntry.from(reader, header.IsPE64);

            lookups.Add(ImportLookupEntry.from(reader, header.IsPE64));
          }
          if (lookups.Count > 0)
          {
            entries = lookups.ToArray();
          }
          Utils.SetPosition(reader, current);
        }
      }
      return entries;
    }
    internal ImportLookupEntry[] WriteEntries(BinaryWriter writer, NTHeader header, UInt32 RVA, ImportLookupEntry[] entries)
    {
      if (writer != null && entries != null && entries.Length > 0)
      {
        Int64 current = Utils.GetPosition(writer);

        UInt32 start = header.GetFilePointer(RVA);

        if (start != 0 && Utils.SetPosition(writer, start))
        {
          for (UInt32 i = 0; i < entries.Length; i++)
          {
            entries[i].Write(writer, header.IsPE64);
          }
          //NO NEED to write terminator
          //ImportLookupEntry.Terminator.Write(writer, header.IsPE64);

          Utils.SetPosition(writer, current);
        }
      }
      return entries;
    }
    #endregion
    #endregion
  }
}
